package com.kfyang.manualgear.generator.declare.orm;

import com.kfyang.manualgear.constant.Constants;
import com.kfyang.manualgear.util.ClassUtils;
import com.kfyang.manualgear.util.ElementUtils;
import com.kfyang.manualgear.util.GlobalConfigUtils;
import com.kfyang.manualgear.annotation.orm.*;
import com.squareup.javapoet.*;
import com.kfyang.manualgear.struct.EntityInfo;
import com.kfyang.manualgear.struct.FieldInfo;
import com.kfyang.manualgear.struct.Handler;
import com.kfyang.manualgear.util.StringUtils;

import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import java.util.ArrayList;
import java.util.List;

import static com.kfyang.manualgear.constant.MethodConstants.*;

public abstract class OrmGenerator {
    protected MethodSpec.Builder methodBuilder;
    protected final List<ExecutableElement> elements;
    protected final EntityInfo entityInfo = new EntityInfo();

    public OrmGenerator(List<ExecutableElement> elements, TypeMirror entityType) {
        this.elements = elements;
        entityInfo.entityType = entityType;
    }

    public List<MethodSpec> build() {
        if (notSupport()) {
            return new ArrayList<>();
        }
        initGlobal();
        return elements.stream()
            .filter(element -> GlobalConfigUtils.methodSet.contains(element.getSimpleName().toString()))
            .map(this::createMethod)
            .toList();
    }

    private MethodSpec createMethod(ExecutableElement element) {
        init(element);
        generateMethod(element);
        copyAnnotations(element);
        return methodBuilder.build();
    }

    protected void copyAnnotations(ExecutableElement element) {
        ElementUtils.copyAnnotations(element, methodBuilder);
    }

    private void generateMethod(ExecutableElement element) {
        switch (element.getSimpleName().toString()) {
            case SELECT_BY_ID -> generateSelectMethod(true);
            case SELECT_ALL -> generateSelectMethod(false);
            case INSERT -> generateInsertMethod(true);
            case INSERT_ALL -> generateInsertMethod(false);
            case UPDATE -> generateUpdate();
            case DELETE_BY_ID -> generateDeleteMethod(true);
            case DELETE_BY_IDS -> generateDeleteMethod(false);
        }
    }


    abstract protected void generateSelectMethod(boolean forOne);

    abstract protected void generateInsertMethod(boolean forOne);

    abstract protected void generateUpdate();

    abstract protected void generateDeleteMethod(boolean forOne);

    private void initGlobal() {
        TypeElement entityClass = ClassUtils.mirrorToType(entityInfo.entityType);
        entityInfo.entityName = entityClass.getSimpleName().toString();

        String qualifiedName = entityClass.getQualifiedName().toString();
        String entityPackage = qualifiedName.substring(0, qualifiedName.lastIndexOf(Constants.DOT));

        // 获取表名
        Table tableAnno = entityClass.getAnnotation(Table.class);
        if (tableAnno != null) {
            entityInfo.tableName = tableAnno.value();
        } else {
            entityInfo.tableName = StringUtils.entityToTableName(entityInfo.entityName);
        }
        entityInfo.entityTypeName = ClassName.get(entityPackage, entityInfo.entityName);

        // 迭代生成字段列表
        while (entityClass != GlobalConfigUtils.objectElement) {
            for (Element enclosedElement : entityClass.getEnclosedElements()) {
                if (enclosedElement.getKind() == ElementKind.FIELD && enclosedElement.getAnnotation(NotCol.class) == null) {
                    String field = enclosedElement.getSimpleName().toString();
                    Col colAnno = enclosedElement.getAnnotation(Col.class);
                    String col = (colAnno != null) ? colAnno.value() : StringUtils.fieldToColumn(field);

                    Handler handler = GlobalConfigUtils.getTypeHandler(enclosedElement.asType().toString());
                    FieldInfo fieldInfo = new FieldInfo(col, field, handler);
                    if (enclosedElement.getAnnotation(Id.class) != null) {
                        entityInfo.idInfo = fieldInfo;
                    } else if (enclosedElement.getAnnotation(LogicalDelete.class) != null) {
                        entityInfo.deleteInfo = fieldInfo;
                    } else if (enclosedElement.getAnnotation(CreateTime.class) != null) {
                        entityInfo.createTimeInfo = fieldInfo;
                    } else if (enclosedElement.getAnnotation(UpdateTime.class) != null) {
                        entityInfo.updateTimeInfo = fieldInfo;
                    } else {
                        entityInfo.fieldInfos.add(fieldInfo);
                    }
                }
            }
            entityClass = ClassUtils.mirrorToType(entityClass.getSuperclass());
        }
    }

    // 初始化方法
    private void init(ExecutableElement element) {
        methodBuilder = MethodSpec.methodBuilder(element.getSimpleName().toString())
            .addModifiers(Modifier.PUBLIC)
            .returns(TypeName.get(element.getReturnType()))
            .addParameters(element.getParameters().stream().map(ParameterSpec::get).toList());
    }

    private boolean notSupport() {
        return entityInfo.entityType == null;
    }
}
